package com.yanqu.road.dao.db;

import com.yanqu.road.entity.CkOrderBy;
import com.yanqu.road.entity.CkPartition;
import com.yanqu.road.entity.CkSkuId;
import com.yanqu.road.entity.FieldTypeAnnotation;
import com.yanqu.road.entity.log.ServerBaseLog;
import com.yanqu.road.entity.log.maidian.BaseDataAttributeCustomGet;
import com.yanqu.road.utils.LogUtils;
import com.yanqu.road.utils.classutil.ScanningFileHelper;
import com.yanqu.road.utils.string.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.*;
import java.util.Date;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description
 * @Author cwq
 * @Data 2022/4/26 16:06
 */
public class DBTableInfoMgr {

    private static Logger log = LogManager.getLogger(DBTableInfoMgr.class);

    private static DBTableInfoMgr instance = new DBTableInfoMgr();

    /**
     * 数据表表信息,须被初始化
     */
    private Map<Class, TableInfo> tableInfoMap = new ConcurrentHashMap<Class, TableInfo>();

    /**
     * k:类名
     */
    private Map<String, TableInfo> classNameTableInfoMap = new ConcurrentHashMap<String, TableInfo>();


    /**
     * 该库连接池名称
     */
    private String connName;

    private boolean showSql = true;

    public static DBTableInfoMgr getInstance() {
        return instance;
    }


    /**
     * 初始化
     * 1.扫描需要动态注入的类
     * 2.动态注入其sql
     */
    public void init(String connName, String packageNames, boolean updateTable) {
        init(connName, packageNames, updateTable, "", "");
    }

    /**
     * 初始化(clickhouse)
     * 1.扫描需要动态注入的类
     * 2.动态注入其sql
     */
    public void init(String connName, String packageNames, boolean updateTable, String database, String cluster) {
        this.connName = connName;
        try {
            // 校验下包下ck相关的
            checkLogFieldDuplicate(packageNames);

            register(packageNames, updateTable, database, cluster);
        } catch (Exception e) {
            log.error("数据库注册失败!", e);
            System.exit(1);
        }
    }

    public static void main(String[] args) {
        // 校验下包下ck相关的
        checkLogFieldDuplicate("com.yanqu.road.entity.log");
    }

    /**
     * 注册类到数据库表服务,
     *
     * @param packageNames 注册整个包下所有需要的文件
     * @param updateTable  是否更新数据库表结构
     */
    private void register(String packageNames, boolean updateTable, String database, String cluster) throws Exception {
        Set<String> classNames = new HashSet<>();
        log.warn("packageNames:{}", packageNames);
        for (String packageName : packageNames.split(",")) {
            ScanningFileHelper scanningFileHelper = new ScanningFileHelper();
            List<Class> classes = scanningFileHelper.searchClasses(packageName, true);
            for (Class<?> aClass : classes) {
                classNames.add(aClass.getName());
            }
        }
        log.warn("classNames size:{}", classNames.size());
        Set<String> loadedTableNames = new HashSet<>();
        for (String className : classNames) {
            readClass(className, updateTable, loadedTableNames, database, cluster);
        }
    }

    //
    private void readClass(String className, boolean updateTable, Set<String> loadedTableNames, String database, String cluster) throws Exception {
//        log.warn("className:{}", className);
        Class<?> cls = Class.forName(className);
        FieldTypeAnnotation clsTypeAnnotation = cls.getAnnotation(FieldTypeAnnotation.class);
        if (clsTypeAnnotation == null || !clsTypeAnnotation.autoCreateTable()) {
            return;
        }
        if (clsTypeAnnotation != null && !clsTypeAnnotation.autoSave()) {
            // 不需要保存
            return;
        }
        // ck日志
        if (this.connName.equals(DbNameStrategy.getCKLogDb()) && !clsTypeAnnotation.uploadCk()) {
            // ck的判断下是否需要处理表结构
            return;
        }
        // 是否跨服日志
        boolean isCrossLog = false;
        // 兼容一下旧版本
        if (className.contains("log.cross") || clsTypeAnnotation.isCross()) {
            isCrossLog = true;
        }
        // 游戏服日志
        if (this.connName.equals(DbNameStrategy.getLogDb()) && (isCrossLog || clsTypeAnnotation.isChannelLog())) {
            return;
        }
        // 跨服日志
        if ((this.connName.equals(DbNameStrategy.getCrossDb()) || this.connName.equals(DbNameStrategy.getCross2Db())) && !isCrossLog) {
            return;
        }
        //跨域日志
        if (this.connName.equals(DbNameStrategy.getChannelServerDb()) && !clsTypeAnnotation.isChannelLog()){
            return;
        }

        String tableName = null;
        List<String> orderByList = new ArrayList<>();
        ArrayList<DbFieldInfo> fieldInfoList = new ArrayList<>();
        ArrayList<String> pkList = new ArrayList<>();
        ArrayList<String> indexList = new ArrayList<>();
        String ckPartition = null;
        String ckSkuId = null;
        Map<String, Class<? extends BaseDataAttributeCustomGet>> customAttributeMap = new HashMap<>();

        if (!StringUtils.isNullOrEmpty(clsTypeAnnotation.tableName())) {
            tableName = clsTypeAnnotation.tableName();
        } else {
            throw new RuntimeException("未定义表名！className: " + className);
        }
        if (loadedTableNames.contains(tableName)) {
            throw new RuntimeException("数据库表重名！tableName:" + tableName + " className:" + className);
        }
        loadedTableNames.add(tableName);
        List<Field> allField = new ArrayList<>();
        Class<?> tempCls = cls;
        while (tempCls != null) {
            CkOrderBy ckOrderBy = tempCls.getAnnotation(CkOrderBy.class);
            if (ckOrderBy != null) {
                List<String> list = StringUtils.stringToStringList(ckOrderBy.value(), ",");
                for (String item : list) {
                    String orderBy = item.trim();
                    if (!orderByList.contains(orderBy)) {
                        orderByList.add(orderBy);
                    }
                }
            }
            CkPartition partition = tempCls.getAnnotation(CkPartition.class);
            if (partition != null) {
                ckPartition = partition.value();
            }
            CkSkuId skuId = tempCls.getAnnotation(CkSkuId.class);
            if (skuId != null) {
                ckSkuId = skuId.value();
            }
            allField.addAll(Arrays.asList(tempCls.getDeclaredFields()));
            tempCls = tempCls.getSuperclass();
        }

        for (Field field : allField) {
            FieldTypeAnnotation fieldTypeAnnotation = field.getAnnotation(FieldTypeAnnotation.class);
            if (fieldTypeAnnotation == null) {
                continue;
            }
            Class classType = field.getType();
            String dbName = field.getName();
            if (!StringUtils.isNullOrEmpty(fieldTypeAnnotation.columnName())) {
                dbName = fieldTypeAnnotation.columnName();
            }
            boolean isSaveJson = false;
            int sqlType = 0;
            if (fieldTypeAnnotation.sqlType() != Types.NULL) {
                sqlType = fieldTypeAnnotation.sqlType();
            } else {
                sqlType = getType(field.getType());
            }
            if (fieldTypeAnnotation.isPk()) {
                pkList.add(dbName);
            }
            if (fieldTypeAnnotation.isIndex()) {
                indexList.add(dbName);
            }
            if (fieldTypeAnnotation.saveJson() || classType == List.class || classType == Map.class) {
                isSaveJson = true;
            }
            DbFieldInfo dbFieldInfo = new DbFieldInfo(
                    fieldTypeAnnotation.insert(),
                    classType,
                    sqlType,
                    field.getName(),
                    dbName,
                    isSaveJson,
                    field,
                    fieldTypeAnnotation.desc(),
                    fieldTypeAnnotation.autoIncrement(),
                    fieldTypeAnnotation.dataAttribute()
            );
            fieldInfoList.add(dbFieldInfo);
            if (fieldTypeAnnotation.custom() != BaseDataAttributeCustomGet.class) {
                customAttributeMap.put(fieldTypeAnnotation.dataAttribute(), fieldTypeAnnotation.custom());
            }
        }
        if (fieldInfoList.size() == 0) {
            throw new RuntimeException("定义的表 没有字段内容！tableName:" + tableName + " className:" + className);
        }

        // 组装问号
        String tmpValue = "";
        String fieldDbNames = "";
        for (int i = 0; i < fieldInfoList.size(); i++) {
            fieldDbNames += fieldInfoList.get(i).getDbName();
            if (i != fieldInfoList.size() - 1) {
                fieldDbNames += "`,`";
            }
            tmpValue += "?,";
        }
        if (tmpValue.length() > 0) {
            tmpValue = tmpValue.substring(0, tmpValue.length() - 1);
        }
        // sql insert 语句
        String insertSql = "INSERT INTO " + tableName
                + "(`" + fieldDbNames + "`) "
                + "VALUES(" + tmpValue + ")";

//        if (showSql && log.isInfoEnabled()) {
//            log.info(insertSql);
//        }

        TableInfo tableInfo = new TableInfo();
        tableInfo.setTableName(tableName);
        tableInfo.setCls(cls);
        tableInfo.setSaveSql(insertSql);
        tableInfo.setPkList(pkList);
        tableInfo.setIndexList(indexList);
        tableInfo.setFieldInfoList(fieldInfoList);
        tableInfo.setDataUpload(clsTypeAnnotation.dataUpload());
        tableInfo.setDb(clsTypeAnnotation.dbName());
        tableInfo.setOrderByList(orderByList);
        tableInfo.setcKPartition(ckPartition);
        tableInfo.setSkuId(ckSkuId);
        tableInfo.setUploadCk(clsTypeAnnotation.uploadCk());
        if (clsTypeAnnotation.dataUpload()) {
            if (StringUtils.isNullOrEmpty(clsTypeAnnotation.eventName())) {
                throw new RuntimeException("埋点缺少 eventName");
            }
        }
        if (clsTypeAnnotation.uploadCk()) {
            if (!ServerBaseLog.class.isAssignableFrom(cls)) {
                throw new RuntimeException("class:" + className + ",未继承 ServerBaseLog");
            }
            if (StringUtils.isNullOrEmpty(clsTypeAnnotation.desc())) {
                throw new RuntimeException("class:" + className + ",日志表加个描述吧,desc用于生成ck模版");
            }
        }
        tableInfo.setEventName(clsTypeAnnotation.eventName());
        tableInfo.setCustomAttributeMap(customAttributeMap);
        if (updateTable) {
            try {
                updateTable(tableName, tableInfo, database, cluster);
            } catch (Throwable t) {
                LogUtils.ERROR.error("表结构更新失败！！！", t);
            }

        }
        tableInfoMap.put(cls, tableInfo);
        classNameTableInfoMap.put(className, tableInfo);
    }

    public TableInfo getTableInfoByClass(Class cls) {
        return tableInfoMap.get(cls);
    }

    public TableInfo getTableInfoByClassName(String className) {
        return classNameTableInfoMap.get(className);
    }

    /**
     * 更新 数据库表
     *
     * @throws SQLException
     */
    private void updateTable(String tableName, TableInfo tableInfo, String database, String cluster) throws SQLException {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        StringBuilder sb = new StringBuilder();

        try {
            if (this.connName.equals(DbNameStrategy.getCKLogDb())) {
                // clickhouse
                 conn = DbPoolMgr.getInstance().getConn();
            } else {
                // mysql
                DBManager db = DatabaseMgr.getDbManager();
                db.setConnName(connName);
                conn = db.getConnection();
            }
            stmt = conn.createStatement();
            rs = stmt.executeQuery("select * from " + tableName + " limit 0");
            ResultSetMetaData rsmd = rs.getMetaData();
            // 字段对应的类型
            HashMap<String, Integer> colTypeMap = new HashMap();
            boolean added = false;
            for (int i = 1; i <= rsmd.getColumnCount(); i++) {
                String colName = rsmd.getColumnName(i);
                int colType = rsmd.getColumnType(i);
                colTypeMap.put(colName, colType);
            }
            rs.close();
            rs = null;
            // 比较结构
            boolean addCol = true;
            if (this.connName.equals(DbNameStrategy.getCKLogDb())) {
                // alter table 表名 on cluster 集群名称 add column new_column_name DataType sync;
                sb.append("ALTER TABLE `").append(tableName).append("` ").append("on cluster ").append(cluster);
            } else {
                sb.append("ALTER TABLE `").append(tableName).append("` ");
            }
            // 比较结构
            for (int i = 0; i < tableInfo.getFieldInfoList().size(); i++) {
                DbFieldInfo dbFieldInfo = tableInfo.getFieldInfoList().get(i);
                Integer colType = colTypeMap.get(dbFieldInfo.getDbName());
                int fieldSqlType = dbFieldInfo.getSqlType();
                if (colType != null) {
                    // 比较字段 属性
                    if (isSameType(colType, fieldSqlType)) {
                        continue;
                    }
                    if (this.connName.equals(DbNameStrategy.getCKLogDb())) {
                        // 为了 支持ck 的类型转换
                        if (colType == Types.VARCHAR
                                && (fieldSqlType == Types.LONGVARCHAR || fieldSqlType == Types.LONGNVARCHAR || fieldSqlType == Types.TIMESTAMP)) {
                            continue;
                        }
                    }
                    addCol = false;
                }
                if (added) {
                    sb.append(",");
                } else {
                    added = true;
                }
                sb.append(addCol ? " ADD" : " MODIFY").append(" COLUMN `").append(dbFieldInfo.getDbName()).append("` ");
                if (this.connName.equals(DbNameStrategy.getCKLogDb())) {
                    sb.append(getClickhouseColDetail(fieldSqlType, 255));
                } else {
                    sb.append(getColDetail(fieldSqlType, 255));
                }
                String comment = dbFieldInfo.getComment();
                if (comment != null) {
                    sb.append(" COMMENT '").append(comment).append("'");
                }
                addCol = true;
            }
            if (!added) {
                conn.close();
                return;
            }
            sb.append(";");
            if (this.connName.equals(DbNameStrategy.getCKLogDb()) && !StringUtils.isNullOrEmpty(cluster)) {
                // 需要同步分布式表 在表名后加“_dst”
                StringBuilder dstSb = new StringBuilder(sb);
                dstSb.insert(dstSb.indexOf(tableName) + tableName.length(), "_dst");
                sb.append(dstSb);
            }
            stmt.close();
            stmt = null;
        } catch (SQLSyntaxErrorException e) {// 表不存在
            createMysqlTable(sb, tableName, tableInfo);
        } catch (SQLException e) {// ClickHouse表不存在
            createClickHouseTable(sb, tableName, tableInfo, database, cluster);
        } catch (Throwable e) {
//            LogUtils.ERROR.error("更新表结构出错! tableName" + tableName, e);
            throw new RuntimeException("更新表结构出错! tableName:" + tableName, e);
        } finally {
            if (rs != null) {
                rs.close();
            }
            if (stmt != null) {
                stmt.close();
            }
        }
        if (showSql && log.isInfoEnabled()) {
            log.info("执行sql：{}", sb.toString());
        }
        try {
            stmt = conn.createStatement();

            int executeUpdate = stmt.executeUpdate(sb.toString());

            stmt.close();
            stmt = null;
            conn.close();
            conn = null;
        } catch (Exception e) {
            throw new RuntimeException("更新表结构出错! tableName:" + tableName, e);
        } finally {
            if (stmt != null) {
                stmt.close();
            }
            if (conn != null) {
                conn.close();
            }
        }
    }

    /**
     * @param types
     * @param len
     * @return
     */
    public String getColDetail(int types, int len) {
        switch (types) {
            case Types.INTEGER:
                return "int(11) NOT NULL DEFAULT 0";
            case Types.TINYINT:
                return "tinyint(3) NOT NULL DEFAULT 0";
            case Types.SMALLINT:
                return "smallint(5) NOT NULL DEFAULT 0";
            case Types.BIGINT:
                return "BIGINT(20) NOT NULL DEFAULT 0";
            case Types.VARCHAR:
                return "varchar(" + len + ") default ''";
            case Types.LONGVARCHAR:
                return "text NOT NULL";
            case Types.TIMESTAMP:
                return "datetime NOT NULL";
            case Types.BIT:
                return "tinyint(1) NOT NULL DEFAULT 0";
        }
        return "text NOT NULL";
    }

    /**
     * @param fieldClass
     * @return
     */
    public static int getType(Class fieldClass) {
        if (int.class.equals(fieldClass)) {
            return Types.INTEGER;
        } else if (byte.class.equals(fieldClass)) {
            return Types.TINYINT;
        } else if (short.class.equals(fieldClass)) {
            return Types.SMALLINT;
        } else if (long.class.equals(fieldClass)) {
            return Types.BIGINT;
        } else if (String.class.equals(fieldClass)) {
            return Types.VARCHAR;
        } else if (List.class.equals(fieldClass) || Map.class.equals(fieldClass)) {
            return Types.LONGVARCHAR;
        } else if (BigInteger.class.equals(fieldClass) || BigDecimal.class.equals(fieldClass)) {
            return Types.VARCHAR;
        } else if (boolean.class.equals(fieldClass)) {
            return Types.TINYINT;
        } else if (Date.class.equals(fieldClass) || Timestamp.class.equals(fieldClass)) {
            return Types.TIMESTAMP;
        }
        return Types.LONGVARCHAR;
    }

    public boolean isSameType(int colType, int fieldSqlType) {
        if (colType == Types.BIT) {
            return Types.TINYINT == fieldSqlType;
        }
        return colType == fieldSqlType;
    }

    public String getClickhouseColDetail(int types, int len) {
        switch (types) {
            case Types.INTEGER:
                return "Int32";
            case Types.TINYINT:
                return "Int8";
            case Types.SMALLINT:
                return "Int16";
            case Types.BIGINT:
                return "Int64";
            case Types.VARCHAR:
                return "String Default ''";
            case Types.BOOLEAN:
                return "Int8";
        }
        return "String Default ''";
    }


    private void createMysqlTable(StringBuilder sb, String tableName, TableInfo tableInfo) {
        try {
            sb.append("CREATE TABLE  `").append(tableName).append("` (").append("`");
            boolean added = false;
            String comment = null;
            for (int i = 0; i < tableInfo.getFieldInfoList().size(); i++) {
                DbFieldInfo dbFieldInfo = tableInfo.getFieldInfoList().get(i);
                String name = dbFieldInfo.getDbName();
                if (!added) {
                    added = true;
                } else {
                    sb.append(",`");
                }
                sb.append(name).append("` ");
                sb.append(getColDetail(dbFieldInfo.getSqlType(), 255));
                if ((comment = dbFieldInfo.getComment()) != null) {
                    sb.append(" COMMENT '").append(comment).append("'");
                }
            }
            // 创建主键
            if (!tableInfo.getPkList().isEmpty()) {
                StringBuilder primaryKeySb = new StringBuilder();
                for (int i = 0; i < tableInfo.getPkList().size(); i++) {
                    if (i > 0) {
                        primaryKeySb.append(",");
                    }
                    primaryKeySb.append("`").append(tableInfo.getPkList().get(i)).append("`");
                }
                sb.append(",PRIMARY KEY (").append(primaryKeySb).append(")");
            }
            // 创建联合索引
            //   KEY `activityId_unionId` (`activityId`,`unionId`) USING BTREE
            if (!tableInfo.getIndexList().isEmpty()) {
                StringBuilder unionIndexSb = new StringBuilder();
                StringBuilder unionIndexNameSb = new StringBuilder();
                for (int i = 0; i < tableInfo.getIndexList().size(); i++) {
                    if (i > 0) {
                        unionIndexSb.append(",");
                        unionIndexNameSb.append("_");
                    }
                    unionIndexSb.append("`").append(tableInfo.getIndexList().get(i)).append("`");
                    unionIndexNameSb.append(tableInfo.getIndexList().get(i));
                }
                sb.append(",KEY `").append(unionIndexNameSb).append("` (").append(unionIndexSb).append(") USING BTREE");
            }
            sb.append(") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void createClickHouseTable(StringBuilder sb, String tableName, TableInfo tableInfo, String database, String cluster) {
        // 如果是集群需要 建本地表 和 分布式表
        // 建本地表
        // CREATE TABLE `xq_test.xq_table1` on cluster DQ_CK_SERVERS
        // (id Int32,sku_id String,create_time Datetime)
        // engine = ReplicatedMergeTree('/clickhouse/tables/{shard}/xq_table1','{replica}') primary key (id);
        // 分布式表
        // create table if not exists xq_test.xq_table1_dst  on cluster dq_ck_servers
        // as xq_test.xq_table1
        // engine = Distributed(dq_ck_servers,xq_test, xq_table1,hiveHash(sku_id));

        // clickhouse 必须要有设置注解 order by
        if (tableInfo.getOrderByList().isEmpty()) {
            throw new RuntimeException("clickhouse 的表没有设置order by tableName:" + tableInfo.getTableName());
        }
        if (StringUtils.isNullOrEmpty(tableInfo.getSkuId())) {
            throw new RuntimeException("clickhouse 的表没有设置skuId tableName:" + tableInfo.getTableName());
        }

        // ck索引（orderby + 日志表索引 isIndex）
        List<String> indexList = new ArrayList<>();
        for (String indexField : tableInfo.getIndexList()) {
            if (!indexList.contains(indexField)) {
                indexList.add(indexField);
            }
        }
        for (String orderField : tableInfo.getOrderByList()) {
            if (!indexList.contains(orderField)) {
                indexList.add(orderField);
            }
        }

        // 分片键 必须是整数
        sb.append("CREATE TABLE IF NOT EXISTS  `").append(tableName).append("`").append(" ON CLUSTER ").append(cluster);
        sb.append("(");
        String comment = null;
        for (int i = 0; i < tableInfo.getFieldInfoList().size(); i++) {
            DbFieldInfo dbFieldInfo = tableInfo.getFieldInfoList().get(i);
            String name = dbFieldInfo.getDbName();
            sb.append("`").append(name).append("` ");
            sb.append(getClickhouseColDetail(dbFieldInfo.getSqlType(), 255));
            if ((comment = dbFieldInfo.getComment()) != null) {
                sb.append(" COMMENT '").append(comment).append("'");
            }
            if (i < tableInfo.getFieldInfoList().size() - 1) {
                sb.append(",");
            }
        }
        sb.append(")engine = ReplicatedMergeTree('/clickhouse/tables/{shard}/{database}/{table}','{replica}') ");
        if (tableInfo.getcKPartition() != null) {
            sb.append(" PARTITION BY ").append(tableInfo.getcKPartition());
        }
        sb.append(" ORDER BY (").append(StringUtils.listToString(indexList, ",")).append(")");
        sb.append(" SETTINGS index_granularity = 8192;");

//        sb.append("\n");

        String dstTableName = tableName + "_dst";
        sb.append("CREATE TABLE IF NOT EXISTS  `").append(dstTableName).append("`").append(" ON CLUSTER ").append(cluster);
        sb.append(" as ").append(tableName);
        sb.append(" engine = Distributed(").append(cluster).append(",").append(database).append(",")
                .append(tableName).append(",").append("hiveHash( `").append(tableInfo.getSkuId()).append("`));");
    }

    /**
     * 校验一下日志类，上传ck的
     */
    public static void checkLogFieldDuplicate(String packageNames) {
        ScanningFileHelper scanningFileHelper = new ScanningFileHelper();
        // 扫描日志类
        List<Class> classes = scanningFileHelper.searchClasses(packageNames, true);
        // 去重一下
        Set<Class> classSet = new HashSet<>(classes);
        Set<String> tableNameSet = new HashSet<>();
        Set<String> tableDescSet = new HashSet<>();
        for (Class<?> cls : classSet) {
            int indexCnt = 0;
            FieldTypeAnnotation clsTypeAnnotation = cls.getAnnotation(FieldTypeAnnotation.class);
            if (clsTypeAnnotation == null || !clsTypeAnnotation.uploadCk()) {
                continue;
            }
            if (!tableNameSet.add(clsTypeAnnotation.tableName())) {
                throw new RuntimeException("日志表名重复,className:" + cls.getName() + ",表名:" + clsTypeAnnotation.tableName());
            }
            if (!tableDescSet.add(clsTypeAnnotation.desc())) {
                throw new RuntimeException("日志描述重复,className:" + cls.getName() + ",表名:" + clsTypeAnnotation.tableName());
            }
            Set<String> fieldNameSet = new HashSet<>();
            Class<?> tempCls = cls;
            while (tempCls != null) {
                Field[] declaredFields = tempCls.getDeclaredFields();
                for (Field field : declaredFields) {
                    FieldTypeAnnotation fieldTypeAnnotation = field.getAnnotation(FieldTypeAnnotation.class);
                    if (fieldTypeAnnotation == null) {
                        continue;
//                        throw new RuntimeException("fieldTypeAnnotation 注解为空,className:" + cls.getName() + ",fieldName:" + field.getName());
                    }
                    if (StringUtils.isNullOrEmpty(fieldTypeAnnotation.desc())) {
                        throw new RuntimeException("字段加个desc吧,className:" + cls.getName() + ",fieldName:" + field.getName());
                    }
                    if (!fieldNameSet.add(field.getName())) {
                        throw new RuntimeException("日志字段重复,className:" + cls.getName() + ",fieldName:" + field.getName());
                    }
                    if (fieldTypeAnnotation.isIndex()) {
                        indexCnt++;
                    }
                }
                tempCls = tempCls.getSuperclass();
            }
            if (indexCnt == 0) {
                throw new RuntimeException("日志类没有设置索引,className:" + cls.getName());
            }
        }
    }

}
